package com.durian.app.lifecycle.apt;

import com.durian.app.lifecycle.annotation.RouterName;
import com.durian.app.lifecycle.annotation.RouterPath;
import com.durian.app.lifecycle.annotation.RouterService;

import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.tools.JavaFileObject;

/**
 * 说明：RouterPathProcessor
 * <p/>
 * 作者：fanly
 * <p/>
 * 类型：Class
 * <p/>
 * 时间：2019-07-05 16:20
 * <p/>
 * 版本：version 1.0
 */
public class RouterProcessor extends AbstractProcessor {

    Messager mMessager;
    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        mMessager = processingEnvironment.getMessager();
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.RELEASE_8;
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> set = new LinkedHashSet<>();
        set.add(RouterPath.class.getCanonicalName());
        set.add(RouterService.class.getCanonicalName());
        set.add(RouterName.class.getCanonicalName());
        return set;
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        Map<String,RouterCreator> nameMap = new HashMap<>();

        Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(RouterName.class);
        for (Element element : elements) {
            if (!element.getKind().isClass()) {
                throw new RuntimeException("Annotation RouterName can only be used in class.");
            }
            TypeElement typeElement = (TypeElement) element;
            String routerName = element.getAnnotation(RouterName.class).value();
            if (!isNotEmpty(routerName)){
                throw new RuntimeException(typeElement.getSimpleName() + " value is null !!!"+" 不允许为null");
            }
            //该类的全限定类名
            String fullClassName = typeElement.getQualifiedName().toString();
            if (!nameMap.containsKey(fullClassName)) {
                List<RouterCreator.PathBuilder> path = findRouterPath(element,roundEnvironment);
                List<RouterCreator.ServiceBuilder> service = findRouterService(element,roundEnvironment);
                RouterCreator creator = new RouterCreator(routerName,path,service);
                nameMap.put(fullClassName, creator);
            }
        }

        for (Map.Entry<String, RouterCreator> entry : nameMap.entrySet()) {
            RouterCreator creator = entry.getValue();
            try {
                JavaFileObject jfo = processingEnv.getFiler().createSourceFile(creator.getProxyClassFullName());
                Writer writer = jfo.openWriter();
                writer.write(creator.generateJavaCode());
                writer.flush();
                writer.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return true;
    }

    private List<RouterCreator.ServiceBuilder> findRouterService(Element element,RoundEnvironment roundEnvironment){
        List<RouterCreator.ServiceBuilder> list = new ArrayList<>();
        List<? extends Element> elements = element.getEnclosedElements();
        //遍历所有使用了该注解的元素
        for (Element el : elements) {
            //如果该注解不是用在类上面，直接抛出异常，该注解用在方法、字段等上面，我们是不支持的
            if (!el.getKind().isField()) {
                continue;
            }
            VariableElement serviceTypeElement = (VariableElement) el;
            RouterService routerService = serviceTypeElement.getAnnotation(RouterService.class);
            if (routerService == null){
                continue;
            }
            if (serviceTypeElement.getConstantValue() == null || "".equals(serviceTypeElement.getConstantValue().toString())){
                throw new RuntimeException(serviceTypeElement.getSimpleName() + " filed value is null !!!"+" 不允许为null");
            }
            RouterCreator.ServiceBuilder builder = new RouterCreator.ServiceBuilder();
            builder.setValue(serviceTypeElement.getConstantValue().toString())
                    .setFiledName(serviceTypeElement.getSimpleName().toString())
                    .setServiceName(routerService.serviceName())
                    .setMethodName(routerService.methodName());
            list.add(builder);
        }
        return list;
    }

    private List<RouterCreator.PathBuilder> findRouterPath(Element element,RoundEnvironment roundEnvironment){
        List<RouterCreator.PathBuilder> list = new ArrayList<>();
        List<? extends Element> elements = element.getEnclosedElements();
        //遍历所有使用了该注解的元素
        for (Element el : elements) {
            if (!el.getKind().isField()) {
                continue;
            }
            VariableElement pathTypeElement = (VariableElement) el;
            RouterPath routerPath = pathTypeElement.getAnnotation(RouterPath.class);
            if (routerPath == null){
                continue;
            }
            if (pathTypeElement.getConstantValue() == null || "".equals(pathTypeElement.getConstantValue().toString())){
                throw new RuntimeException(pathTypeElement.getSimpleName() + " filed value is null !!!"+" 不允许为null");
            }
            RouterCreator.PathBuilder builder = new RouterCreator.PathBuilder();
            builder.setValue(pathTypeElement.getConstantValue().toString())
                    .setFragment(routerPath.fragment())
                    .setTransfer(routerPath.transfer())
                    .setExtras(routerPath.extras())
                    .setMethodName(routerPath.methodName())
                    .setParams(routerPath.params())
                    .setFiledName(pathTypeElement.getSimpleName().toString());
            list.add(builder);
        }
        return list;
    }

    public static boolean isNotEmpty(String str){
        return str != null && !"".equals(str);
    }
}
